package com.mingqijia.gassafety.shared.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.MediaType;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gap.basic.base.CommonResponse;
import com.gap.basic.exception.BizException;
import com.mingqijia.gassafety.shared.constant.Constants;
import com.mingqijia.gassafety.shared.enums.ManagerType;
import com.mingqijia.gassafety.shared.user.UserBackend;
import com.mingqijia.gassafety.shared.user.UserFactory;

import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONObject;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author Damon S.
 */
@Slf4j
@Component
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ApplicationUtils implements ApplicationContextAware {

    private static final SecureRandom SECURE = new SecureRandom();

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private static final PasswordEncoder encoder = new BCryptPasswordEncoder();

    public static ApplicationContext CTX;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        ApplicationUtils.CTX = context;
    }

    public static <T> Collection<T> getBeansOfType(Class<T> clazz) {
        return CTX.getBeansOfType(clazz).values();
    }

    public static String getUserId() {
        return getWorkingUser().getUserId();
    }

    /** 获取header中的spId */
    public static String getHeaderSpId() {
        String headerSpId = (String) getRequest().getAttribute(Constants.HEADER_SP_ID);
        if (org.apache.commons.lang3.StringUtils.isEmpty(headerSpId)) {
            headerSpId = getAttrFromHeader(Constants.HEADER_SP_ID);
        }
        return headerSpId;
    }

    /** 获取 当前用户正在操作的SpId --> 改为获取header 中的 spId */
    public static String getWorkingSpId() {
        return getHeaderSpId();
    }

    public static ManagerType getUserType() {
        return ApplicationUtils.getWorkingUser().getType();
    }

    /** 获取header中的参数 */
    public static String getAttrFromHeader(String key) {
        String header = getRequest().getHeader(key);
        log.info(key + ":{}", header);
        return header;
    }

    /** 获取当前管理用户 */
    public static UserBackend getWorkingUser() {
        UserFactory factory = CTX.getBean(UserFactory.class);
        if (ObjectUtils.isEmpty(factory)) {
            throw new BizException("用户未登录");
        }
        return factory.getPrincipal();
    }

    /** 用于处理refresh token时，组装返回结果中的罪行token（使用SecurityContext中的用户对象） */
    public static JSONObject getTokenObject() {
        UserFactory factory = CTX.getBean(UserFactory.class);
        if (ObjectUtils.isEmpty(factory)) {
            throw new BizException("用户未登录");
        }
        return factory.getObject();
    }

    /** 未加盐的密码加密 */
    public static String encodePwd(String rawPwd, String username) {
        return encoder.encode(DigestUtil.md5Hex(rawPwd + username));
    }

    /** 已经加盐的密码加密 */
    public static String encodePwd(String password) {
        return encoder.encode(password);
    }

    public static <T> T toBean(String rawJson, Class<T> clazz) {
        T result;
        try {
            result = MAPPER.readValue(rawJson, clazz);
        }
        catch (Exception e) {
            log.error("解析JSON数据异常[rawJson={}]", rawJson, e);
            throw new BizException("解析JSON数据异常");
        }
        return result;
    }

    public static <T> T toBean(InputStream is, Class<T> clazz) {
        T result;
        try {
            result = MAPPER.readValue(is, clazz);
        }
        catch (Exception e) {
            log.error("解析JSON数据异常[rawJson={}]", is.toString(), e);
            throw new BizException("请检查JSON参数");
        }
        return result;
    }

    public static String toJson(Object source) {
        String result;
        try {
            if (source instanceof String) {
                return (String) source;
            }
            result = MAPPER.writeValueAsString(source);
        }
        catch (Exception e) {
            log.error("对象数据序列化异常[rawJson={}]", source, e);
            throw new BizException("对象数据序列化异常");
        }
        return result;
    }

    public static void forceChangePwd(HttpServletResponse response) throws IOException {
        Map<String, Boolean> params = new HashMap<>();
        params.put("forceChangePwd", true);
        writeOutJson(response, CommonResponse.success(params));
    }

    /** 返回图片 */
    public static void writeOutImage(HttpServletResponse response, byte[] image, String ext) throws IOException {
        response.setContentType("image/" + ext.replace(Constants.STR_COMMA, Constants.STR_EMPTY));
        try (OutputStream output = response.getOutputStream()) {
            output.write(image);
            output.flush();
        }
    }

    /** 返回字符串 */
    public static void writeOutJson(HttpServletResponse response, Object message) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        try (PrintWriter out = response.getWriter()) {
            out.write(toJson(message));
            out.flush();
        }
    }

    /** 获取请求 */
    public static HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Assert.notNull(attributes, "非web请求途径");
        return attributes.getRequest();
    }

    /** 获取响应 */
    public static HttpServletResponse getResponse() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Assert.notNull(attributes, "非web请求途径");
        return attributes.getResponse();
    }

    public static String pureUuid() {
        return UUID.randomUUID().toString().replace(Constants.STR_STRIKETHROUGH, Constants.STR_EMPTY);
    }

    /** 获取6位随机数字验证码 */
    public static String getRandomInt(int count) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < count; i++) {
            builder.append(SECURE.nextInt(10));
        }
        return builder.toString();
    }

    public static String substring(String message, int length) {
        if (StringUtils.hasLength(message) && message.length() >= length) {
            int startIndex = SECURE.nextInt(message.length() - length);
            return message.substring(startIndex, startIndex + length);
        }
        throw new RuntimeException("截取子串的输入参数不合法。");
    }

    public static String lenUpperString(int length) {
        return substring(pureUuid(), length).toUpperCase();
    }

    public static String lenLowerString(int length) {
        return lenUpperString(length).toLowerCase();
    }

    public static String getRequestHeaders() {
        StringBuilder headers = new StringBuilder();
        try {
            HttpServletRequest request = getRequest();
            Enumeration<String> enumeration = request.getHeaderNames();
            while (enumeration.hasMoreElements()) {
                String name = enumeration.nextElement();
                String value = request.getHeader(name);
                headers.append(",").append(name).append(":").append(value);
            }
        }
        catch (Exception e) {
            log.error("ApplicationUtils.getRequestHeaders ERROR ", e);
            throw e;
        }
        return headers.substring(1);
    }
}
